package cn.donting.jme.swing.tab;

import cn.donting.jme.EditorContent;
import cn.donting.jme.core.state.CameraLookAtState;
import cn.donting.jme.editor.scene.Scene;
import cn.donting.jme.editor.scene.SceneCamera;
import cn.donting.jme.event.camera.SelectCameraEvent;
import cn.donting.jme.event.scene.SceneChangeEvent;
import cn.donting.jme.event.scene.SceneSaveEvent;
import cn.donting.jme.event.spatial.SelectSpatialEvent;
import cn.donting.jme.expansion.builder.light.LightAttributeBuilder;
import cn.donting.jme.expansion.builder.mesh.MeshAttributeBuilder;
import cn.donting.jme.swing.JDragTree;
import cn.donting.jme.swing.Tab;
import cn.donting.jme.swing.drag.DataTransferable;
import cn.donting.jme.swing.drag.EditorDataFlavor;
import cn.donting.jme.utile.CloneUtil;
import cn.donting.jme.utile.ErrorUtil;
import cn.donting.jme.utile.IconUtil;
import cn.donting.jme.utile.SaveUtil;
import com.formdev.flatlaf.extras.FlatSVGIcon;
import com.google.common.eventbus.Subscribe;
import com.jme3.light.*;
import com.jme3.material.Material;
import com.jme3.material.Materials;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.filechooser.FileFilter;
import javax.swing.tree.*;
import java.awt.*;
import java.awt.datatransfer.Transferable;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

@Slf4j
public class SceneTreeTabOld extends Tab implements TreeCellRenderer, TreeSelectionListener {
    private static final String spatialTreeNodeDataKey = "SpatialTreeNode";

    public static final int tabIndex = 3;
    @Getter
    private Scene scene;
    @Getter
    private File file;
    private DefaultTreeModel treeModel;
    private JDragTree tree;
    private Clipboard clipboard;
    private JLabel loadingLabel;
    private JScrollPane jScrollPane;

    public SceneTreeTabOld() {
        setTitle("场景");
        setLayout(new BorderLayout());
        tree = new JDragTree();
        jScrollPane = new JScrollPane(tree);
        add(jScrollPane, BorderLayout.CENTER);
        tree.setRootVisible(false);
        tree.setShowsRootHandles(true);
        tree.setCellRenderer(this::getTreeCellRendererComponent);
        tree.setDragEnabled(true);
        tree.addTreeSelectionListener(this::valueChanged);
        tree.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                if (SwingUtilities.isRightMouseButton(e)) {
                    // 获取右键点击的路径
                    TreePath path = tree.getClosestPathForLocation(e.getX(), e.getY());
                    if (path != null) {
                        tree.setSelectionPath(path);
                        tree.requestFocus();
                    }
                }
                TreePath selectionPath = tree.getSelectionPath();
                if (selectionPath == null) {
                    return;
                }
                Object lastPathComponent = selectionPath.getLastPathComponent();
                if (SwingUtilities.isRightMouseButton(e) && lastPathComponent instanceof SpatialTreeNode) {
                    showSpatialMenu((SpatialTreeNode) lastPathComponent, e);
                    return;
                }
                if (e.getClickCount() == 2 && SwingUtilities.isLeftMouseButton(e) && lastPathComponent instanceof SpatialTreeNode) {
                    SpatialTreeNode spatialTreeNode = (SpatialTreeNode) lastPathComponent;
                    //聚焦 相机到 目标位置
                    Spatial spatial = spatialTreeNode.spatial;
                    CameraLookAtState cameraLookAtState = EditorContent.getJmeApplication().getStateManager().getState(CameraLookAtState.class);
                    cameraLookAtState.lookAt(EditorContent.getJmeApplication().getEditorCamera(), spatial);
                }
            }
        });

        tree.setTransferHandler(new TreeTransferHandler());
        tree.addKeyListener(new KeyAdapter() {
            @Override
            public void keyReleased(KeyEvent e) {
                if ((e.getKeyCode() == KeyEvent.VK_S) && ((e.getModifiers() & KeyEvent.CTRL_MASK) != 0)) {
                    try {
                        log.info("save scene:{}", file);
                        SaveUtil.save(file, scene);
//                        EditorContent.postEvent(new SceneSaveEvent(SceneTreeTab.this, scene, file));
                    } catch (IOException ex) {
                        ErrorUtil.showErrorDialog(ex);
                    }
                }
            }
        });
        EditorContent.registerEvent(this);

        loadingLabel = new JLabel("加载中...");
        loadingLabel.setHorizontalAlignment(SwingUtilities.CENTER);
    }

    @Override
    public void setLoading() {
        remove(jScrollPane);
        add(loadingLabel);
        loadingLabel.setText("加载中....");
    }

    @Subscribe
    private void sceneChangeEvent(SceneChangeEvent changeEvent) {
        if (!getTitle().endsWith("*")) {
            setTitle(getTitle() + "*");
        }
    }

    @Subscribe
    private void sceneSaveEvent(SceneSaveEvent sceneSaveEvent) {
        if (getTitle().endsWith("*")) {
            setTitle(getTitle().substring(0, getTitle().length() - 1));
        }
    }

    private void showSpatialMenu(SpatialTreeNode spatialTreeNode, MouseEvent e) {
        Spatial spatial = spatialTreeNode.spatial;
        JPopupMenu jPopupMenu = new JPopupMenu();
        JMenuItem refresh = new JMenuItem("刷新", new FlatSVGIcon("icon/refresh.svg"));
        refresh.addActionListener(e1 -> {
            refreshSpatialTreeNode(spatialTreeNode);
        });
        refresh.setEnabled(spatial instanceof Node);
        jPopupMenu.add(refresh);
        jPopupMenu.add(new JPopupMenu.Separator());

//==========================================================================

        JMenu newObj = new JMenu("新建");
        newObj.setIcon(new FlatSVGIcon("icon/addFile.svg"));
        newObj.setEnabled(spatial instanceof Node);
        jPopupMenu.add(newObj);
        JMenuItem newNode = new JMenuItem(Node.class.getSimpleName(), new FlatSVGIcon("icon/node.svg"));
        newObj.add(newNode);
        newNode.addActionListener(e16 -> {
            EditorContent.runJme(() -> {
                Node node = new Node("new Node");
                ((Node) spatial).attachChild(node);
                EditorContent.runSwing(() -> {
                    SpatialTreeNode newTreeNode = new SpatialTreeNode(node);
                    spatialTreeNode.add(newTreeNode);
                    treeModel.nodeStructureChanged(spatialTreeNode);
                    EditorContent.postEvent(new SceneChangeEvent(tree));
                });

            });
        });
        newObj.add(new JPopupMenu.Separator());
        List<MeshAttributeBuilder> meshAttributeBuilders = EditorContent.getIocContainer().getBeansByTypes(MeshAttributeBuilder.class);
        for (MeshAttributeBuilder meshAttributeBuilder : meshAttributeBuilders) {
            Mesh mesh = meshAttributeBuilder.newInstance();
            JMenuItem newMesh = new JMenuItem(meshAttributeBuilder.getName(), meshAttributeBuilder.getIcon(16));
            newObj.add(newMesh);
            newMesh.addActionListener(e15 -> {
                EditorContent.runJme(() -> {
                    Geometry geometry = new Geometry("new " + mesh.getClass().getSimpleName());
                    geometry.setMesh(mesh);
                    Material material = new Material(EditorContent.getJmeApplication().getAssetManager(), Materials.LIGHTING);
                    geometry.setMaterial(material);
                    Camera camera = EditorContent.getJmeApplication().getEditorCamera();
                    Vector3f direction = camera.getDirection();
                    Vector3f wordPost = camera.getLocation().add(direction.mult(5));
                    //世界坐标转本地坐标
                    Vector3f pos = spatial.worldToLocal(wordPost, null);
                    geometry.setLocalTranslation(pos);
                    ((Node) spatial).attachChild(geometry);
                    EditorContent.runSwing(() -> {
                        SpatialTreeNode newTreeNode = new SpatialTreeNode(geometry);
                        spatialTreeNode.add(newTreeNode);
                        treeModel.nodeStructureChanged(spatialTreeNode);
                        EditorContent.postEvent(new SceneChangeEvent(tree));
                    });
                });
            });
        }
//==========================================================================
        jPopupMenu.add(new JPopupMenu.Separator());

        JMenu lightJMenu = new JMenu("灯光");
        jPopupMenu.add(lightJMenu);

        List<LightAttributeBuilder> lightAttributeBuilders =
                EditorContent.getIocContainer().getBeansByTypes(LightAttributeBuilder.class);
        for (LightAttributeBuilder lightAttributeBuilder : lightAttributeBuilders) {
            JMenuItem lightItem = new JMenuItem(lightAttributeBuilder.getName());
            lightItem.setIcon(lightAttributeBuilder.getActiveIcon(LightAttributeBuilder.ICON_SIZE));
            lightJMenu.add(lightItem);
            lightItem.addActionListener(e18 -> {
                Light light = lightAttributeBuilder.newInstance();
                EditorContent.runJme(() -> {
                    spatial.addLight(light);
                });
            });
        }
        jPopupMenu.add(new JPopupMenu.Separator());

//==========================================================================

        JMenuItem copy = new JMenuItem("复制", new FlatSVGIcon("icon/copy.svg"));
        copy.addActionListener(e12 -> {
            clipboard = new Clipboard(spatialTreeNode, ClipboardType.Copy);
        });
//==========================================================================

        JMenuItem cut = new JMenuItem("剪切", new FlatSVGIcon("icon/cut.svg"));
        cut.setEnabled(spatial != scene.getRootNode());
        copy.addActionListener(e12 -> {
            clipboard = new Clipboard(spatialTreeNode, ClipboardType.Cut);
        });
//==========================================================================

        JMenuItem paste = new JMenuItem("粘贴", new FlatSVGIcon("icon/paste.svg"));
        paste.setEnabled((spatial instanceof Node) && clipboard != null);
        paste.addActionListener(e13 -> {
            SpatialTreeNode source = clipboard.source;
            if (clipboard.type.equals(ClipboardType.Copy)) {
                Spatial newSpatial = CloneUtil.clone(source.spatial, false);
                ((Node) spatial).attachChild(newSpatial);
                SpatialTreeNode newTreeNode = createSpatialTreeNode(new SpatialTreeNode(newSpatial));
                spatialTreeNode.add(newTreeNode);
                treeModel.nodeStructureChanged(spatialTreeNode);
                EditorContent.postEvent(new SceneChangeEvent(tree));
                return;
            }
            if (clipboard.type.equals(ClipboardType.Cut)) {
                SpatialTreeNode parent = (SpatialTreeNode) source.getParent();
                parent.remove(source);
                treeModel.nodeStructureChanged(parent);
                spatialTreeNode.add(source);
                treeModel.nodeStructureChanged(spatialTreeNode);
                Node node = (Node) spatial;
                node.attachChild(clipboard.source.spatial);
                EditorContent.postEvent(new SceneChangeEvent(tree));
            }
        });


//==========================================================================

        JMenuItem delete = new JMenuItem("删除", new FlatSVGIcon("icon/delete.svg"));
        delete.addActionListener(e14 -> {
            Node parent = spatial.getParent();
            parent.detachChild(spatial);
            SpatialTreeNode parent1 = (SpatialTreeNode) spatialTreeNode.getParent();
            parent1.remove(spatialTreeNode);
            treeModel.nodeStructureChanged(parent1);
            EditorContent.postEvent(new SceneChangeEvent(tree));
        });
        delete.setEnabled(spatial != scene.getRootNode());

        jPopupMenu.add(copy);
        jPopupMenu.add(cut);
        jPopupMenu.add(paste);
        jPopupMenu.add(delete);

        jPopupMenu.add(new JPopupMenu.Separator());

//==========================================================================

        JMenu saveTo = new JMenu("保存为");
        jPopupMenu.add(saveTo);

        JMenuItem j3o = new JMenuItem("j3o");
        saveTo.add(j3o);
        j3o.addActionListener(e17 -> {
            JFileChooser fileChooser = new JFileChooser(EditorContent.getProject().getSrcResourcesDir());
            fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
            fileChooser.setSelectedFile(new File(spatial.getName() + ".j3o"));
            fileChooser.setFileFilter(new FileFilter() {
                @Override
                public boolean accept(File f) {
                    return f.isDirectory() || f.getName().endsWith("." + Scene.extName);
                }

                @Override
                public String getDescription() {
                    return "j3o";
                }
            });
            int result = fileChooser.showOpenDialog(tree);
            if (result == JFileChooser.APPROVE_OPTION) {
                File selectedFile = fileChooser.getSelectedFile();
                try {
                    SaveUtil.save(selectedFile, spatial);
                } catch (Exception ex) {
                    ErrorUtil.showErrorDialog(ex);
                }
            }
        });


        jPopupMenu.show(tree, e.getX(), e.getY());
    }

    public void openScene(Scene scene, File file) {
        this.scene = scene;
        this.file = file;
        EditorContent.getEditorUI().getSceneNode().attachChild(scene.getRootNode());
        initScene();
        remove(loadingLabel);
        add(jScrollPane, BorderLayout.CENTER);
    }

    private void initScene() {
        Node rootNode = scene.getRootNode();
        DefaultMutableTreeNode root = new DefaultMutableTreeNode();
        treeModel = new DefaultTreeModel(root);
        root.add(createSpatialTreeNode(new SpatialTreeNode(rootNode)));
        root.add(createCameraTreeNode());
        tree.setModel(treeModel);
    }

    @Override
    protected boolean isPaintingOrigin() {
        return super.isPaintingOrigin();
    }

    private SpatialTreeNode createSpatialTreeNode(SpatialTreeNode prent) {
        if (prent.spatial instanceof Node) {
            for (Spatial child : ((Node) prent.spatial).getChildren()) {
                SpatialTreeNode childNode = new SpatialTreeNode(child);
                prent.add(childNode);
                createSpatialTreeNode(childNode);
            }
        }
        return prent;
    }

    private CameraTreeNode createCameraTreeNode() {
        CameraTreeNode cameraTreeNode = new CameraTreeNode(null);
        ArrayList<SceneCamera> cameras = scene.getCameras();
        for (SceneCamera camera : cameras) {
            cameraTreeNode.add(new CameraTreeNode(camera));
        }
        return cameraTreeNode;
    }

    private void refreshSpatialTreeNode(SpatialTreeNode spatialTreeNode) {
        spatialTreeNode.removeAllChildren();
        createSpatialTreeNode(spatialTreeNode);
        treeModel.nodeStructureChanged(spatialTreeNode);
    }

    @Override
    public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
        JLabel label = new JLabel("root");
        if (value instanceof SpatialTreeNode) {
            Spatial spatial = ((SpatialTreeNode) value).spatial;
            label.setText(spatial.getName());
            label.setIcon(getIcon(spatial));
        }
        if (value instanceof CameraTreeNode) {
            SceneCamera sceneCamera = ((CameraTreeNode) value).sceneCamera;
            if (sceneCamera == null) {
                label.setText("相机列表");
                label.setIcon(new FlatSVGIcon("icon/cameras.svg"));
            } else {
                label.setText(sceneCamera.getCamera().getName());
                label.setIcon(new FlatSVGIcon("icon/camera.svg"));
            }
        }
        return label;
    }

    private Icon getIcon(Spatial spatial) {
        return IconUtil.getIcon(spatial);
    }

    public void nameChange(Spatial spatial) {
        log.info("nameChange:{}", spatial.getName());
        TreeNode treeNode = EditorContent.getObjectData(spatial, spatialTreeNodeDataKey);
        treeModel.nodeChanged(treeNode);
    }
    public void nameChange(SceneCamera sceneCamera) {
        log.info("nameChange:{}", sceneCamera.getCamera().getName());
        TreeNode treeNode = EditorContent.getObjectData(sceneCamera, spatialTreeNodeDataKey);
        treeModel.nodeChanged(treeNode);
    }
    @Override
    public void valueChanged(TreeSelectionEvent e) {
        TreePath selectionPath = tree.getSelectionPath();
        if (selectionPath == null) {
            SelectSpatialEvent selectSpatial = new SelectSpatialEvent(tree, null);
            EditorContent.postEvent(selectSpatial);
            return;
        }
        Object lastPathComponent = selectionPath.getLastPathComponent();
        if (lastPathComponent instanceof SpatialTreeNode) {
            Spatial spatial = ((SpatialTreeNode) lastPathComponent).spatial;
            SelectSpatialEvent selectSpatial = new SelectSpatialEvent(tree, spatial);
            EditorContent.postEvent(selectSpatial);
        }
        if (lastPathComponent instanceof CameraTreeNode) {
            SceneCamera sceneCamera = ((CameraTreeNode) lastPathComponent).sceneCamera;
            SelectCameraEvent selectCameraEvent = new SelectCameraEvent(tree, sceneCamera);
            EditorContent.postEvent(selectCameraEvent);
        }
    }

    private class SpatialTreeNode extends DefaultMutableTreeNode {
        private Spatial spatial;

        public SpatialTreeNode(Spatial spatial) {
            this.spatial = spatial;
            EditorContent.setObjectData(spatial, spatialTreeNodeDataKey, this);
        }

        @Override
        public String toString() {
            return spatial.toString();
        }

        public boolean isNode() {
            return spatial instanceof Node;
        }
    }


    private  class CameraTreeNode extends DefaultMutableTreeNode {
        private SceneCamera sceneCamera;

        public CameraTreeNode(SceneCamera sceneCamera) {
            this.sceneCamera = sceneCamera;
            EditorContent.setObjectData(sceneCamera, spatialTreeNodeDataKey, this);
        }
    }


    private static enum ClipboardType {
        Copy, Cut
    }

    private static class Clipboard {
        private final SpatialTreeNode source;
        private final ClipboardType type;

        public Clipboard(SpatialTreeNode source, ClipboardType type) {
            this.source = source;
            this.type = type;
        }
    }


    private class TreeTransferHandler extends TransferHandler {
        @Override
        public int getSourceActions(JComponent c) {
            return TransferHandler.LINK;
        }

        @Override
        protected Transferable createTransferable(JComponent c) {
            if (tree.getDragPath() != null) {
                TreePath dragPath = tree.getDragPath();
                Object lastPathComponent = dragPath.getLastPathComponent();
                if (lastPathComponent instanceof SpatialTreeNode) {
                    SpatialTreeNode spatialTreeNode = ((SpatialTreeNode) lastPathComponent);
                    return new DataTransferable(spatialTreeNode.spatial, EditorDataFlavor.spatial);
                }
                if (lastPathComponent instanceof CameraTreeNode) {
                    CameraTreeNode cameraTreeNode = ((CameraTreeNode) lastPathComponent);
                    if (cameraTreeNode == null) {
                        return null;
                    }
                    return new DataTransferable(cameraTreeNode.sceneCamera, EditorDataFlavor.camera);
                }
            }
            return null;
        }

        @Override
        public boolean canImport(TransferSupport support) {
            Point dropPoint = support.getDropLocation().getDropPoint();
            double x = dropPoint.getX();
            TreePath path = tree.getClosestPathForLocation((int) x, (int) dropPoint.getY());
            if (path != null) {
                Object lastPathComponent = path.getLastPathComponent();
                if (lastPathComponent instanceof SpatialTreeNode && ((SpatialTreeNode) lastPathComponent).isNode()) {
                    return support.isDataFlavorSupported(EditorDataFlavor.getFileDataFlavor("j3o"));
                }
            }
            return false;
        }

        @Override
        public boolean importData(TransferSupport support) {
            Point dropPoint = support.getDropLocation().getDropPoint();
            double x = dropPoint.getX();
            TreePath path = tree.getClosestPathForLocation((int) x, (int) dropPoint.getY());
            if (path != null) {
                Object lastPathComponent = path.getLastPathComponent();
                if (lastPathComponent instanceof SpatialTreeNode && ((SpatialTreeNode) lastPathComponent).isNode()) {
                    SpatialTreeNode spatialTreeNod = (SpatialTreeNode) lastPathComponent;
                    Spatial spatial = spatialTreeNod.spatial;
                    Transferable transferable = support.getTransferable();
                    try {
                        File file = (File) transferable.getTransferData(EditorDataFlavor.getFileDataFlavor("j3o"));
                        Spatial loadModel = SaveUtil.loadModel(file);
                        EditorContent.runJme(() -> {
                            ((Node) spatial).attachChild(loadModel);
                            EditorContent.runSwing(() -> {
                                SpatialTreeNode neTreeNode = new SpatialTreeNode(spatial);
                                createSpatialTreeNode(neTreeNode);
                                spatialTreeNod.add(neTreeNode);
                                refreshSpatialTreeNode(spatialTreeNod);
                                EditorContent.postEvent(new SceneChangeEvent(tree));
                            });
                        });
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }
            }
            return false;
        }
    }

}
